package main

import (
	"flag"
	"fmt"
	"mysql-sync/internal"
	"os"
	"strings"
)

var (
	configPath          = flag.String("conf", "./conf.json", "json config file path")
	sync                = flag.Bool("sync", false, "sync schema changes to dest's db\non default, only show difference")
	drop                = flag.Bool("drop", false, "drop fields,index,foreign key only on dest's table")
	singleSchemaChange  = flag.Bool("single_schema_change", false, "single schema changes ddl command a single schema change")
	sql2compare         = flag.String("sql_check", "", "sql to compare result on both dsn")
	sqlFile             = flag.String("sql_file", "", "sql file path")
	verbose             = flag.Bool("verbose", false, "enable verbose logging")
	version             = flag.Bool("version", false, "show version information")
	conditionalSync     = flag.Bool("conditional_sync", false, "execute conditional data synchronization")
)

func init() {
	df := flag.Usage
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "MySQL Schema Sync Tool %s\n", internal.Version)
		fmt.Fprintf(os.Stderr, "%s\n\n", internal.AppURL)
		fmt.Fprintf(os.Stderr, "Usage:\n")
		df()
		fmt.Fprintf(os.Stderr, "\nExamples:\n")
		fmt.Fprintf(os.Stderr, "  %s -conf conf.json                    # Show differences only\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "  %s -conf conf.json -sync              # Sync schema changes\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "  %s -conf conf.json -drop -sync        # Sync and drop extra fields\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "  %s -conf conf.json -conditional_sync  # Execute conditional data sync\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "  %s -conf conf.json -sql_check \"SELECT COUNT(*) FROM users\"\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "  %s -conf conf.json -sql_file data.sql\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "\n")
	}
}

var cfg *internal.Config

// showVersion 显示版本信息
func showVersion() {
	fmt.Printf("MySQL Schema Sync Tool\n")
	fmt.Printf("Version: %s\n", internal.Version)
	fmt.Printf("URL: %s\n", internal.AppURL)
}

// 对比两个dsn下的数据库
func compareDSN(cfg *internal.Config) error {
	defer internal.LogExecutionTime("database comparison and sync")()
	
	cfg.Sync = *sync
	cfg.Drop = *drop
	cfg.SingleSchemaChange = *singleSchemaChange
	
	syncInstance, err := internal.NewSchemaSync(cfg)
	if err != nil {
		return internal.NewSyncError("failed to create schema sync instance", err)
	}

	for _, dbname := range cfg.Schemas {
		internal.Info("Processing database: %s", dbname)
		
		if err := syncInstance.UseDb(dbname); err != nil {
			internal.Error("Failed to switch to database %s: %v", dbname, err)
			return err
		}
		
		// 检查数据差异
		if err := syncInstance.CheckDiffData(cfg); err != nil {
			internal.Error("Failed to check data differences for database %s: %v", dbname, err)
			return err
		}
		
		// 检查存储过程
		if err := internal.CheckAlterProcedure(cfg); err != nil {
			internal.Error("Failed to check procedures for database %s: %v", dbname, err)
			return err
		}
		
		// 检查schema差异
		if err := internal.CheckSchemaDiff(cfg); err != nil {
			internal.Error("Failed to check schema differences for database %s: %v", dbname, err)
			return err
		}
	}
	
	return nil
}

// 向目标库导入sql
func importSQL(cfg *internal.Config, file string) error {
	defer internal.LogExecutionTime("SQL import")()
	
	internal.Info("Importing SQL file: %s", file)
	
	sqls, err := os.ReadFile(file)
	if err != nil {
		return internal.NewFileError("failed to read SQL file", err)
	}
	sqlStr := string(sqls)

	if strings.TrimSpace(sqlStr) == "" {
		internal.Warn("SQL file is empty: %s", file)
		return nil
	}

	sqlArr := strings.Split(sqlStr, ";\n")
	sc, err := internal.NewSchemaSync(cfg)
	if err != nil {
		return internal.NewSyncError("failed to create schema sync instance", err)
	}
	
	if err := sc.SyncSQL4Dest(sqlStr, sqlArr); err != nil {
		return internal.NewSyncError("failed to execute SQL", err)
	}
	
	internal.Info("SQL import completed successfully")
	return nil
}

// 对比sql在两个dsn执行的结果
func compareSQL(cfg *internal.Config, sql string) error {
	defer internal.LogExecutionTime("SQL comparison")()
	
	if strings.TrimSpace(sql) == "" {
		return internal.NewValidationError("SQL query cannot be empty", nil)
	}
	
	internal.Info("Comparing SQL query: %s", sql)
	return internal.CompareSqlResult(cfg, sql)
}

// 执行条件同步
func executeConditionalSync(cfg *internal.Config) error {
	defer internal.LogExecutionTime("conditional data synchronization")()
	
	if !cfg.CheckHasConditionalSync() {
		return internal.NewValidationError("No conditional sync configurations found in config file", nil)
	}
	
	internal.Info("Starting conditional data synchronization...")
	
	for _, schema := range cfg.Schemas {
		internal.Info("Processing schema: %s", schema)
		
		// 创建SchemaSync实例
		schemaSync, err := internal.NewSchemaSync(cfg)
		if err != nil {
			return err
		}
		
		// 切换到指定数据库
		if err := schemaSync.UseDb(schema); err != nil {
			return err
		}
		
		// 执行条件同步
		if err := schemaSync.SyncConditionalData(cfg); err != nil {
			return err
		}
	}
	
	internal.Info("Conditional data synchronization completed successfully")
	return nil
}

func main() {
	flag.Parse()
	
	// 显示版本信息
	if *version {
		showVersion()
		return
	}
	
	// 设置日志级别
	if *verbose {
		internal.SetLogLevel(internal.LogLevelDebug)
	}
	
	// 设置panic恢复
	defer internal.SafeRecover()
	
	// 加载配置
	cfg, err := internal.LoadConfig(*configPath)
	if err != nil {
		internal.Fatal("Failed to load configuration: %v", err)
	}
	
	// 根据参数执行不同的操作
	if len(*sql2compare) > 0 {
		// 对比sql的执行结果
		if err := compareSQL(cfg, *sql2compare); err != nil {
			internal.Fatal("SQL comparison failed: %v", err)
		}
	} else if len(*sqlFile) > 0 {
		// 在目标库执行sql文件
		if err := importSQL(cfg, *sqlFile); err != nil {
			internal.Fatal("SQL import failed: %v", err)
		}
	} else if *conditionalSync {
		// 执行条件同步
		if err := executeConditionalSync(cfg); err != nil {
			internal.Fatal("Conditional sync failed: %v", err)
		}
	} else {
		// 对比或同步两个数据库
		if err := compareDSN(cfg); err != nil {
			internal.Fatal("Database comparison/sync failed: %v", err)
		}
	}
	
	internal.Info("Operation completed successfully")
}
